{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "ff6050dfa4dc1b6",
   "metadata": {},
   "source": [
    "## 基于多目标GP的符号回归\n",
    "\n",
    "多目标GP是指使用多个目标函数来评估GP树的适应度。在符号回归问题中，通常使用均方误差（MSE）作为目标函数。然而，MSE并不能很好地反映模型的复杂度，因此，我们还可以使用树的大小作为目标函数。这样，就可以得到更为精简的模型。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "59cfefc0467c74ad",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-11-10T08:50:31.317854700Z",
     "start_time": "2023-11-10T08:50:31.272249300Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\zhenl\\anaconda3\\Lib\\site-packages\\deap\\creator.py:185: RuntimeWarning: A class named 'FitnessMulti' has already been created and it will be overwritten. Consider deleting previous creation of that class or rename it.\n",
      "  warnings.warn(\"A class named '{0}' has already been created and it \"\n",
      "C:\\Users\\zhenl\\anaconda3\\Lib\\site-packages\\deap\\creator.py:185: RuntimeWarning: A class named 'Individual' has already been created and it will be overwritten. Consider deleting previous creation of that class or rename it.\n",
      "  warnings.warn(\"A class named '{0}' has already been created and it \"\n"
     ]
    }
   ],
   "source": [
    "import math\n",
    "import operator\n",
    "import random\n",
    "from deap import base, creator, tools, gp, algorithms\n",
    "\n",
    "# 定义评估函数，包含两个目标：均方误差和树的大小\n",
    "def evalSymbReg(individual,pset):\n",
    "    # 编译GP树为函数\n",
    "    func = gp.compile(expr=individual, pset=pset)\n",
    "    # 计算均方误差（Mean Square Error，MSE）\n",
    "    mse = ((func(x) - x**2)**2 for x in range(-10, 10))\n",
    "    # 计算GP树的大小\n",
    "    size = len(individual)\n",
    "    return math.fsum(mse), size\n",
    "\n",
    "# 修改适应度函数，包含两个权重：MSE和树的大小。MSE是最小化，树的大小也是最小化\n",
    "creator.create(\"FitnessMulti\", base.Fitness, weights=(-1.0, -1.0))\n",
    "creator.create(\"Individual\", gp.PrimitiveTree, fitness=creator.FitnessMulti)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "956e01e17271daa6",
   "metadata": {},
   "source": [
    "### 遗传算子\n",
    "遗传算子基本不需要修改。由于是多目标优化问题，所以选择算子需要使用NSGA2（Non-dominated Sorting Genetic Algorithm II）。\n",
    "NSGA2算法的基本思想是，首先对种群中的个体进行非支配排序，然后根据非支配排序的结果计算拥挤度距离，最后根据非支配排序和拥挤度距离两个指标对个体进行排序。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "851794d4d36e3681",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-11-10T08:50:31.317854700Z",
     "start_time": "2023-11-10T08:50:31.278882Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\zhenl\\anaconda3\\Lib\\site-packages\\deap\\gp.py:254: RuntimeWarning: Ephemeral rand101 function cannot be pickled because its generating function is a lambda function. Use functools.partial instead.\n",
      "  warnings.warn(\"Ephemeral {name} function cannot be \"\n"
     ]
    }
   ],
   "source": [
    "import random\n",
    "\n",
    "# 定义函数集合和终端集合\n",
    "pset = gp.PrimitiveSet(\"MAIN\", arity=1)\n",
    "pset.addPrimitive(operator.add, 2)\n",
    "pset.addPrimitive(operator.sub, 2)\n",
    "pset.addPrimitive(operator.mul, 2)\n",
    "pset.addPrimitive(operator.neg, 1)\n",
    "pset.addEphemeralConstant(\"rand101\", lambda: random.randint(-1, 1))\n",
    "pset.renameArguments(ARG0='x')\n",
    "\n",
    "# 定义遗传编程操作\n",
    "toolbox = base.Toolbox()\n",
    "toolbox.register(\"expr\", gp.genHalfAndHalf, pset=pset, min_=1, max_=2)\n",
    "toolbox.register(\"individual\", tools.initIterate, creator.Individual, toolbox.expr)\n",
    "toolbox.register(\"population\", tools.initRepeat, list, toolbox.individual)\n",
    "toolbox.register(\"compile\", gp.compile, pset=pset)\n",
    "toolbox.register(\"evaluate\", evalSymbReg, pset=pset)\n",
    "toolbox.register(\"select\", tools.selNSGA2)\n",
    "toolbox.register(\"mate\", gp.cxOnePoint)\n",
    "toolbox.register(\"mutate\", gp.mutUniform, expr=toolbox.expr, pset=pset)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "62f30d17704db709",
   "metadata": {},
   "source": [
    "### 算法模块\n",
    "DEAP算法包提供了eaMuPlusLambda函数，可以比较方便地使用NSGA2的环境选择算子。  \n",
    "理想情况下，最好还是自行实现演化函数，这样才能完整地使用NSGA-II算法中的锦标赛选择算子。  \n",
    "NSGA-II算法中的锦标赛选择算子是指，首先从种群中随机选择两个个体，然后根据非支配排序和拥挤度距离两个指标对两个个体进行排序，最后选择排名较高的个体作为父代。简单起见，我们忽略了锦标赛选择算子。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "515b587d4f8876ea",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-11-10T08:50:31.364942900Z",
     "start_time": "2023-11-10T08:50:31.284352200Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "   \t      \t                    fitness                    \t                      size                     \n",
      "   \t      \t-----------------------------------------------\t-----------------------------------------------\n",
      "gen\tnevals\tavg    \tgen\tmax   \tmin\tnevals\tstd    \tavg \tgen\tmax\tmin\tnevals\tstd    \n",
      "0  \t50    \t40468.9\t0  \t160004\t0  \t50    \t20563.8\t4.14\t0  \t7  \t2  \t50    \t1.64936\n",
      "1  \t50    \t33558  \t1  \t42026 \t0  \t50    \t14608.4\t3.66\t1  \t9  \t2  \t50    \t1.62   \n",
      "2  \t50    \t26880.7\t2  \t42026 \t0  \t50    \t18371.7\t3.2 \t2  \t9  \t2  \t50    \t1.48324\n",
      "3  \t50    \t21903.8\t3  \t40666 \t0  \t50    \t19421  \t2.84\t3  \t6  \t2  \t50    \t0.966644\n",
      "4  \t50    \t19668  \t4  \t39336 \t0  \t50    \t19668  \t2.5 \t4  \t3  \t2  \t50    \t0.5     \n",
      "5  \t50    \t1573.44\t5  \t39336 \t0  \t50    \t7708.25\t2.96\t5  \t3  \t2  \t50    \t0.195959\n",
      "6  \t50    \t1573.44\t6  \t39336 \t0  \t50    \t7708.25\t2.96\t6  \t3  \t2  \t50    \t0.195959\n",
      "7  \t50    \t1573.44\t7  \t39336 \t0  \t50    \t7708.25\t2.96\t7  \t3  \t2  \t50    \t0.195959\n",
      "8  \t50    \t1573.44\t8  \t39336 \t0  \t50    \t7708.25\t2.96\t8  \t3  \t2  \t50    \t0.195959\n",
      "9  \t50    \t1573.44\t9  \t39336 \t0  \t50    \t7708.25\t2.96\t9  \t3  \t2  \t50    \t0.195959\n",
      "10 \t50    \t1573.44\t10 \t39336 \t0  \t50    \t7708.25\t2.96\t10 \t3  \t2  \t50    \t0.195959\n",
      "Best individual is:\n",
      " mul(x, x)\n",
      "\n",
      "With fitness: (0.0, 3.0)\n"
     ]
    }
   ],
   "source": [
    "import numpy\n",
    "from deap import algorithms\n",
    "\n",
    "# 统计指标\n",
    "stats_fit = tools.Statistics(lambda ind: ind.fitness.values[0])\n",
    "stats_size = tools.Statistics(lambda ind: ind.fitness.values[1])\n",
    "mstats = tools.MultiStatistics(fitness=stats_fit, size=stats_size)\n",
    "mstats.register(\"avg\", numpy.mean)\n",
    "mstats.register(\"std\", numpy.std)\n",
    "mstats.register(\"min\", numpy.min)\n",
    "mstats.register(\"max\", numpy.max)\n",
    "\n",
    "population = toolbox.population(n=50)\n",
    "pop, log  = algorithms.eaMuPlusLambda(population=population,\n",
    "                           toolbox=toolbox, mu=len(population),lambda_=len(population),\n",
    "                                      cxpb=0.9, mutpb=0.1, ngen=10, stats=mstats, halloffame=None, verbose=True)\n",
    "\n",
    "# 最佳个体\n",
    "best_ind = tools.selBest(pop, 1)[0]\n",
    "print('Best individual is:\\n', best_ind)\n",
    "print('\\nWith fitness:', best_ind.fitness.values)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7aa57e0f8b6151ad",
   "metadata": {},
   "source": [
    "基于优化结果，我们还可以绘制Pareto前沿，以便于选择最终的模型。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "28284e0a0047fcfc",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-11-10T08:50:31.483100600Z",
     "start_time": "2023-11-10T08:50:31.314335800Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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9WfDMGyW//lnAAAAALjZ2BWkli9frlatWik0NFQLFixwdk0AAAAA4NHsukdqxIgROnz4sFMKyMrK0qhRo9SyZUs1btxYjz/+uHbs2HHF+QUFBZo0aZJatGihRo0a6cknn9S+ffucUhsAAAAAFMeuIOXMy/WGDh2qPXv2KCkpSUuWLFGDBg3Up08fHTx4sNj5o0eP1pIlSzR27FgtXbpU5cuXV79+/XT27Fmn1QgAAAAAf2T6qX2OdOjQIW3evFmJiYmKi4vT7bffrpEjRyosLEwrVqwoMv/w4cNasmSJJkyYoNatW6tWrVoaP368fH199d///tcNewAAAACgNLL7YRObNm3SL7/8Ytfchx56yK55oaGhmjdvnqKioqxjFotFhmEoOzu7yPxvvvlGISEhatmypXUsJCREGzZssOvzAAAAAMAR7A5SM2fOtGuexWKxO0iFhISoVatWNmOrVq3Sr7/+qoSEhCLz09LSVL16dX311VeaN2+ejh8/rvr16+ull15SrVq17PrM4hiGofPnz5d4fUfJycmx+RPOQZ+djx67Bn12PnrsGvTZNeiz89Fj13Bmnw3DkMVisWuuxbDjBqi6detqxowZqlevnl0brVq1ql3z/mznzp3q27ev4uPjNWvWrCLLR44cqTVr1ui2227Tiy++qJCQEM2ePVv/+c9/tHLlyiLvtLLH3r17lZ+fX6J6AQAAANxcfH19FR0dfc15dp+RuvXWW0sckOyxbt06DRs2TDExMUpKSip2jo+Pj86ePavJkydbz0BNnjxZrVq10ueff66+ffuW6LN9fHwUERFR4todJScnR2lpaQoPD1dAQIC7y7lp0Wfno8euQZ+djx67Bn12DfrsfPTYNZzZ55SUFLvnmn4hrzMkJydr3Lhxat++vSZOnChfX99i51WuXFne3t42l/H5+/urevXqOnLkSIk/32KxKDAwsMTrO1pAQIBH1XOzos/OR49dgz47Hz12DfrsGvTZ+eixazijz/Ze1ie5+al9krRo0SKNHTtWXbt21ZQpU64YoiQpLi5OFy5c0N69e61jubm5Onz4sGrUqOGKcgEAAADAvjNS69evV6VKlRz+4ampqRo/frzat2+v/v37KyMjw7rM399fgYGBOn36tIKDg+Xv76+4uDjdddddGj58uF577TWVL19e06ZNU5kyZfTXv/7V4fUBAAAA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dPQ155k+I3X69Gn16NFDv/zyi26//XalpKQoNzdXmzZt0htvvKEPPvhAsbGxpgtet26dhg0bppiYGCUlJRU75/XXX1d4eLi6dOlievtX4+Pjo4iICIdusyRycnKUlpam8PBwBQQEuLucmxZ9dj567Br02fnosWvQZ9egz85Hj13DmX1OSUmxe67pIPXWW2/p999/18qVK1W1alXr/U1Tp05Vnz59NG3aNL3//vumtpmcnKxx48apffv2mjhxonx9fYudt3TpUvn6+lqD2sWLFyVJnTp10oMPPqjXXnvN7O5IunRfVmBgYInWdYaAgACPqudmRZ+djx67Bn12PnrsGvTZNeiz89Fj13BGn+29rE8qQZD617/+pZdfflk1atSwBhlJ8vPzU+/evfXSSy+Z2t6iRYs0duxYdevWTS+//LK8vK78/IuvvvrK5uc9e/bohRde0Lx581SrVi1zOwIAAAAAJWQ6SOXl5al8+fLFLitTpowKCgrs3lZqaqrGjx+v9u3bq3///srIyLAu8/f3V2BgoE6fPq3g4GD5+/urRo0aNuunp6dLkm677TZVrFjR7K4AAAAAQImYfvx5dHS0Fi1aVOyyL7/80uZR5teyZs0aFRQUaO3atUpISLD5GjdunI4dO6aEhAStXLnSbJkAAAAA4DSmz0g999xz6tmzp/7617+qVatWslgsWrFihaZPn65vvvlG7777rt3bGjBggAYMGHDVOfv377/ismbNml11OQAAAAA4g+kzUnFxcXr//fcVEBCgd999V4Zh6IMPPtDJkyc1d+5c3Xnnnc6oEwAAAAA8RoleyNukSRN98sknys3NVXZ2toKCglS2bFlH1wYAAAAAHsn0GakRI0Zox44dki49ECIsLMwaovbt26d27do5tkIAAAAA8DCmg9Tnn3+unj176tNPPy2yLD8/X0ePHnVIYQAAAADgqUwHKUlq1aqVEhMTNWbMGJt3SQEAAABAaVCiINW/f38lJSVZz05lZWU5uCwAAAAA8FwlClKS1LFjRy1cuFC//vqrHn30Ue3fv1/e3iV6dgUAAAAA3FBKHKQkqUGDBlqyZIluueUWdenSRZs3b3ZUXQAAAADgsa4rSElSpUqVtGDBAt1zzz1KSkpyRE0AAAAA4NFMX4v3zDPPKCwszGbM19dXb775purWrasNGzY4rDgAAAAA8EQlClJX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cAXcIwUApVBgYKBatWqlVatWFVm2cuVKdejQQd7etn/XVlhYqHnz5ql9+/aKiorSvffeqwULFtjMuXjxoubNm6dOnTqpYcOGatSokbp06aItW7ZY50yfPl3t27fXxo0b9cADD1i39fnnn1+15lOnTkmSDMMosqx///56+umnlZ+fbx377rvv1LlzZ8XExKhDhw5av3692rdvr+nTp0uSjhw5osjIyCKXyL300ktq27ZtifZpxowZatasme6++25lZmZKkhYvXqz7779fUVFRat26taZPn64LFy5cdV/NaNu2rcaPH68ePXqocePGGjVqlLZu3arIyEh98sknatOmje666y598803kqTNmzfriSee0B133KFmzZrp+eef17Fjx6zbW7ZsmerXr6/FixcrISFBLVu21M8//+ywegHgZkGQAoBSqmPHjtqzZ4+OHj1qHTt37py+/vprderUqcj80aNHa9q0aXrwwQc1Z84cdejQQePHj9fMmTOtcyZOnKiZM2eqc+fOevfdd/Xaa68pMzNTzz33nM6fP2+dd/LkSb322mvq3r275s2bp2rVqumll17SwYMHr1hvixYt5O3trR49emjGjBnavXu3CgoKJEkNGzZUnz59FBAQIEn64Ycf1LdvX5UtW1ZTp07Vk08+qZdfftkaxsywd5+OHj2qtWvXKikpSYMHD1ZoaKjmzp2rV199VfHx8ZozZ466du2qd955R6NGjbrm5xqGoQsXLhT79ecwuXDhQkVGRmr69On661//ah2fPHmyhg8fruHDh6tRo0b64osv1Lt3b4WFhSkpKUkjRozQrl271LlzZ2VkZFjXu3jxoubMmaPXX39dgwcPVkREhOm+AcDNjkv7AKCUat26tQIDA7V69Wr17t1bkrR27VpVqFBBd9xxh83c1NRUffbZZxo6dKieeuopSVJCQoIsFovmzp2rJ554QqGhoTpx4oSGDBmibt26Wdf19/fXs88+q/379ys2NlbSpfuwxo0bp/j4eElSeHi42rRpo02bNqlWrVrF1hsZGanJkydrzJgxmj59uqZPny5/f3/FxcXp0UcfVceOHa1z586dqwoVKmjOnDny9fWVJIWEhOiFF14w3Sd79+nChQsaPny47rrrLknS2bNnNXv2bHXu3FmvvPKKtWfly5fXK6+8ol69eql27dpX/Nzt27erQYMGxS57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",
      "text/plain": [
       "<Figure size 1000x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from matplotlib import pyplot as plt\n",
    "import seaborn as sns\n",
    "\n",
    "# 非支配排序\n",
    "fronts = tools.sortNondominated(pop, len(pop), first_front_only=True)\n",
    "\n",
    "# Pareto前沿\n",
    "pareto_front = fronts[0]\n",
    "fitnesses = [ind.fitness.values for ind in pareto_front]\n",
    "\n",
    "# 分离均方误差和树的大小\n",
    "mse = [fit[0] for fit in fitnesses]\n",
    "sizes = [fit[1] for fit in fitnesses]\n",
    "\n",
    "# 使用seaborn绘制散点图\n",
    "sns.set(style=\"whitegrid\")\n",
    "plt.figure(figsize=(10, 6))\n",
    "sns.scatterplot(x=mse, y=sizes, palette=\"viridis\", s=60, edgecolor=\"w\", alpha=0.7)\n",
    "plt.xlabel('Mean Square Error')\n",
    "plt.ylabel('Size of the GP Tree')\n",
    "plt.title('Pareto Front')\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
